//============================================================================
// Copyright (C) 2013 Brett R. Jones
//
// Issued to public domain 2013
//
// brettjones1900@gmail.com
// http://www.p2panarchy.com
//============================================================================

#include <string>
#include <vector>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "VxUtilLib.h"
#include "VxFileUtil.h"
#include "VxParse.h"
#include "VxFileIsTypeFunctions.h"

#include <sys/stat.h>
#ifdef WIN_PLATFORM
	#include "shlobj.h" // for VxGetMyDocumentsDir
	#include <direct.h>
#else
	#include <dirent.h> // for searching directories
	#include <ctype.h>
	#include <unistd.h> 
#endif

//============================================================================--
RCODE VxGetCurrentWorkingDirectory( std::string strRetDir )
{
	char* buffer;
	if( (buffer = getcwd( NULL, 0 )) == NULL )
	{
		strRetDir = "";
		log_msg( LOG_DEBUG, "VxGetCurrentWorkingDirectory: getcwd error\n" );
		return -1;
	}
	else
	{
		strRetDir = buffer;
        free(buffer);
		return 0;
	}
}

//============================================================================--
RCODE VxSetCurrentWorkingDirectory( const char * pDir )
{
	return chdir(pDir);
}

//============================================================================--
//! returns file size or 0 if doesnt exist
U64 VxFileExists( const char * pFileName )
{
	int result;
#ifdef WIN_PLATFORM
	struct __stat64 gStat;
	// Get data associated with the file
	result = _stat64( pFileName, &gStat );
#else
	struct stat64 gStat;
	// Get data associated with the file
	result = stat64( pFileName, &gStat );
#endif //WIN_PLATFORM

	// Check if statistics are valid:
	if( result != 0 )
	{
		//error getting file info
		return 0;
	}
	else
	{
		//return file size
		return gStat.st_size;
	}
}
//============================================================================--
//! return true if directory exists
bool VxDirectoryExists( const char * pDir )
{
	char acBuf[ MAX_PATH ];
	strcpy( acBuf, pDir );
	bool bIsDir = TRUE;
	struct stat oFileStat;

	if( strlen( acBuf ) > 3 )
	{
		//if not root of drive remove the trailing backslash
		if( ('/' == acBuf[ strlen( acBuf ) - 1 ]) ||
			('\\' == acBuf[ strlen( acBuf ) - 1 ]) )
		{
			acBuf[ strlen( acBuf ) - 1 ] = 0;
		}
	}
	memset( &oFileStat, 0, sizeof( struct stat ) );
#ifdef WIN_PLATFORM
	oFileStat.st_mode = _S_IFDIR; //check for dir not file
	if( 0 == stat( acBuf, &oFileStat ) )
	{
		if( FALSE == ( oFileStat.st_mode & _S_IFDIR ))
		{
			//path is not valid directory
			bIsDir = FALSE;
		}
	}
	else
	{
		bIsDir = FALSE;
	}
#else // LINUX
	oFileStat.st_mode = S_IFDIR; //check for dir not file
	if( 0 == stat( acBuf, &oFileStat ) )
	{
		if( FALSE == ( oFileStat.st_mode & S_IFDIR ))
		{
			//path is not valid directory
			bIsDir = FALSE;
		}
	}
	else
	{
		bIsDir = FALSE;
	}
#endif // LINUX
	return bIsDir;
}

//============================================================================--
//! Make all directories that don't exist in a given path
RCODE VxMakeDirectory( const char * pDirectoryPath )
{
   vx_assert( pDirectoryPath );
   char tempDir[MAX_PATH];
   char *pTemp = tempDir;
   //make a copy
   strcpy(tempDir, pDirectoryPath);
   //replace '\' with '/'
   while((pTemp = strchr(tempDir, '\\')))
   {
        pTemp[0] = '/';
   }
   //if no '/' at end put it there
   if( '/' != tempDir[strlen(tempDir) - 1] )
   {
        strcat(tempDir , "/");
   }
   //make the path
   pTemp = tempDir;
   #ifdef WIN_PLATFORM
		// for windows .. if root drive skip it
		if( ':' == pTemp[1] )
		{
			pTemp = strchr( pTemp, '/' );
			if( pTemp )
			{
				pTemp++;
			}
		}
	#endif //WIN_PLATFORM
   if( pTemp && strlen( pTemp ) )
   {
		while((pTemp = strtok(pTemp, "/" )))
		{
				//look for drive letter or root path
				if(0 == strlen(tempDir))
				{
					continue;
				}
				if(!VxDirectoryExists(tempDir))
				{
					//make directory
					if( ! (0 == mkdir( tempDir, S_IRWXU | S_IRWXG | S_IRWXO )))
					{
						log_msg( 0, "VxUtilLib:FailedToMakeDir %s\n", tempDir );
						return -1;
					}
				}
				//move pTemp up
				pTemp = tempDir + strlen(tempDir);
				//put the '/' back
				tempDir[strlen(tempDir)] = '/';
		}
   }
   return 0;
}
//============================================================================--
//! read a line from file into buffer and null terminate it
RCODE VxReadLine( FILE *pgFile, char *pBuf, int iBufLen )
{
    char c;
    int i= 0;

    //=== Read a line  ===//
	// skip over \n or \r if is the first character
	c = getc( pgFile );
	if( c == '\n' || c == '\r' )
	{
		c = getc( pgFile );
	}
    while( c != '\n' && c != '\r' )
    {
        if( c == EOF )
		{
			pBuf[i] = 0;
            return EOF;
		}
        if( i < iBufLen )
		{
            pBuf[i++] = c;
		}
		else
		{
			log_msg( 0, "Read Line Buffer Overflow\n" );
			return EOF; // not enough room in buffer
		}
		c = getc( pgFile );
    }
    pBuf[i] = '\0';
    return 0;
}
//============================================================================--
//! open a file and log error message if fails
FILE *VxFileOpen( const char * pFileName, const char* pFileMode )
{
    FILE * retval;
    retval = fopen(pFileName,pFileMode);
	if( NULL == retval )
	{
		log_msg( 0, "VxFileOpen:Could not open file %s\n", pFileName );
	}
    return retval;
}
//============================================================================--
//! File seek..NOTE: only seeks from beginning of file
RCODE VxFileSeek ( FILE * poFile, U32 u32Pos )
{
	return fseek( poFile, u32Pos, SEEK_SET );
}
//============================================================================--
//! File seek..NOTE: only seeks from beginning of file
RCODE VxFileSeek ( FILE * poFile, U64 u64Pos )
{
#ifdef WIN_PLATFORM
	return _fseeki64( poFile, u64Pos, SEEK_SET );
#else
	return fseek( poFile, u64Pos, SEEK_SET );
#endif// WIN_PLATFORM
}

//============================================================================--
//! Copy a file
RCODE VxCopyFile( const char * pOldPath, const char * pNewPath )
{
	#ifdef WIN_PLATFORM
		if( CopyFile( pOldPath, pNewPath, FALSE ) )
		{
			return 0;
		}
		return VxGetLastError();
	#else // LINUX
		VxDeleteFile( pNewPath );
		char as8Buf[ MAX_PATH * 2 ];
		sprintf( as8Buf, "cp %s %s\n", pOldPath, pNewPath );
		system( as8Buf );
		unlink( pOldPath );
		return 0;
	#endif // LINUX
}
//============================================================================--
//! Delete a file
RCODE VxDeleteFile( const char * pFileName )
{
	#ifdef WIN_PLATFORM
		return _unlink( pFileName );
	#else // LINUX
		return unlink( pFileName );
	#endif // LINUX
}
//============================================================================--
//! Rename a file
RCODE VxRenameFile( const char * pFileOldName, const char * pFileNewName )
{
	#ifdef WIN_PLATFORM
		return rename( pFileOldName, pFileNewName );
	#else // LINUX
		return rename( pFileOldName, pFileNewName );
	#endif // LINUX
}
//============================================================================--
//! copy files to destination directory then delete the source files
RCODE VxMoveFiles( char * pDestDir, char * pSrcDir )
{
	// make directory if doesnt exits
	VxMakeDirectory( pDestDir );
	// loop through files in source directory and move them

	char as8DestDir[ MAX_PATH * 2 ];
	char as8DestFile[ MAX_PATH * 2 ];
	strcpy( as8DestDir, pDestDir );

	// build path and wild card
	char as8SrcDir[ MAX_PATH * 2 ];
	char as8SrcFile[ MAX_PATH * 2 ];
	strcpy( as8SrcDir, pSrcDir );
	#ifdef WIN_PLATFORM
		strcat( as8SrcDir, "\\*.*" );
	#else // LINUX
		// do nothing..
	#endif
	log_msg( 0, "VxMoveFile: moving files from %s to %s\n", as8SrcDir, as8DestDir );

	#ifdef WIN_PLATFORM
		// start working for files
		WIN32_FIND_DATA FindFileData;

		HANDLE hFind = FindFirstFile( as8SrcDir, &FindFileData);
		if (hFind == INVALID_HANDLE_VALUE)
		{
			// done with listing
			return 0;
		}
		BOOL bFinished = FALSE;
		while ( FALSE == bFinished)
		{
			// skip . and .. files; otherwise, we'd
			// recur infinitely!
			if('.' == FindFileData.cFileName[0 ])
			{
				if( FALSE == FindNextFile(hFind, &FindFileData)  )
					break;
				continue;
			}
			// make source file name
			strcpy( as8SrcFile, pSrcDir  );
			strcat( as8SrcFile, "\\" );
			strcat( as8SrcFile, FindFileData.cFileName );
			// make destination file name
			strcpy( as8DestFile, pDestDir  );
			strcat( as8DestFile, "\\" );
			strcat( as8DestFile, FindFileData.cFileName );

			struct stat m;
			if( 0 != stat( as8SrcFile, &m) )
			{
				///ERROR how do we handle
				continue;
			}
			if( _S_IFDIR & m.st_mode )
			{
				// its a directory
				continue;
			}
			// its a file
			if( 0 == VxCopyFile( as8SrcFile, as8DestFile ) )
			{
				VxDeleteFile( as8SrcFile );
			}
			if( FALSE == FindNextFile(hFind, &FindFileData)  )
			{
				// done with listing
				FindClose(hFind);
				return 0;
			}
		}
		return 0;
	#else //LINUX
		// find the files in the directory
		DIR *pDir;
		struct dirent *pFileEnt;
		if( VxDirectoryExists( as8SrcDir ) )
		{
			log_msg( 0, "VxMoveFile: directory %s exists.. opening dir\n", as8SrcDir );
			//ok directory exists!
			if(!(NULL == (pDir = opendir(as8SrcDir))))
			{
				//pDir is open
				while( 0 != (pFileEnt = readdir(pDir)))
				{
					log_msg( 0, "VxMoveFile: found file %s\n", pFileEnt->d_name );
					//got a file or directory
					if( '.' == pFileEnt->d_name[ 0 ] )//||
				//			(DT_LNK == pFileEnt->d_type )  ||
				//			(DT_UNKNOWN == pFileEnt->d_type ) )
					{
						// skip . and ..
						log_msg( 0, "VxMoveFile: skipping file %s\n", pFileEnt->d_name );
						continue;
					}
					// valid directory entry
					// make source file name
					strcpy( as8SrcFile, as8SrcDir );
					if( '/' != as8SrcFile[ strlen( as8SrcFile ) - 1 ] )
					{
						strcat( as8SrcFile, "/" );
					}
					strcat( as8SrcFile, pFileEnt->d_name );
					log_msg( 0, "VxMoveFile: made src file %s\n", as8SrcFile );
					// make destination file name
					strcpy( as8DestFile, as8DestDir );
					if( '/' != as8DestFile[ strlen( as8DestFile ) - 1 ] )
					{
						strcat( as8DestFile, "/" );
					}
					strcat( as8DestFile, pFileEnt->d_name );
					log_msg( 0, "VxMoveFile: made dest file %s\n", as8DestFile );
					//=== Last Modification Date ===//
					struct stat m;
					if( 0 != stat( as8SrcFile, &m) )
					{
						///ERROR how do we handle
						log_msg( 0, "Unable to stat file %s\n", as8SrcFile );
						continue;
					}
					if( pFileEnt->d_type == DT_DIR )
					{
						// its a directory
						log_msg( 0, "file %s is directory\n", as8SrcFile );
						continue;
					}
					// its a file
					log_msg( 0, "VxMoveFile: moving file %s to file %s\n", as8SrcFile, as8DestFile );
					if( 0 == VxCopyFile( as8SrcFile, as8DestFile ) )
					{
						VxDeleteFile( as8SrcFile );
					}
					else
					{
						log_msg( 0, "VxMoveFile: FAILED moving file %s to file %s\n", as8SrcFile, as8DestFile );
					}
				}
				// end of listing
				// done with listing
				closedir(pDir);
				return 0;
			}
			else
			{
				log_msg( 0, "VxMoveFiles: could not open directory %s \n", as8SrcDir );
			}
		}
		else
		{
			log_msg( 0, "VxMoveFiles: directory %s does not exist \n", as8SrcDir );
		}
		return 0;
	#endif //LINUX
}
//============================================================================--
//! separate Path and file name into separate strings
RCODE	VxSeperatePathAndFile(	std::string &	strFullPath,		// path and file name			
								std::string &	strRetPath,		// return path to file
								std::string &	strRetFile )	// return file name
{
	return VxSeperatePathAndFile( strFullPath.c_str(), strRetPath, strRetFile );
}
//============================================================================--
//! separate Path and file name into separate strings
RCODE	VxSeperatePathAndFile(	const char *	pFullPath,		// path and file name			
								std::string &	strRetPath,		// return path to file
								std::string &	strRetFile )	// return file name
{
	char as8Buf[ MAX_PATH ];
	strcpy( as8Buf, pFullPath );
	VxMakeForwardSlashPath( as8Buf );
	char * pForwardSlash = strrchr( as8Buf, '/' );
	if( pForwardSlash )
	{
		// get file name
		strRetFile = &pForwardSlash[1];
		// get path
		pForwardSlash[1] = 0;
		strRetPath = as8Buf;
		return 0;
	}
	vx_assert( false );
	return -1;
}

//============================================================================--
//! remove the path and return just the file name
void	VxGetJustFileName(		const char *	pFullPath,	// file name may be full or just file name
								std::string&	strRetJustFileName )		// return file name
{
	std::string	strRetPath;
	RCODE rc = VxSeperatePathAndFile(	pFullPath,		// path and file name			
										strRetPath,		// return path to file
										strRetJustFileName );	// return file name
	if( rc )
	{
		log_msg( LOG_ERROR, "VxGetJustFileName: error %d file %s\n", rc, pFullPath );
		strRetJustFileName = pFullPath;
	}
}

//============================================================================--
//! get the . extension of file name
void	VxGetFileExtension(		std::string&	strFileName,	// file name with extension
								std::string&	strRetExt )		// return extension ( ie "myfile.etm" would return etm"
{
	int iIdx;
	if( -1 != (iIdx = StdStringReverseFind( strFileName, '.') ) )
	{
		strRetExt = &(((const char *)strFileName.c_str())[iIdx + 1]);
	}
	else
	{
		//ErrMsgBox("No File Extension Found %s\n", csFileName.c_str() );
		strRetExt = "";
	}
}

//============================================================================--
//! flip back slashes into forward slashes
void VxMakeForwardSlashPath( std::string & csFilePath )
{
	char * pTemp = (char *)csFilePath.c_str();
	size_t iLen = strlen( pTemp );
	for( size_t i = 0; i < iLen; i++ )
	{
		if( '\\' == pTemp[i] )
			pTemp[i] = '/';
	}
}
//============================================================================--
//! flip back slashes into forward slashes
void VxMakeForwardSlashPath( char * pFilePath )
{
	size_t iLen = strlen( pFilePath );
	for( size_t i = 0; i < iLen; i++ )
	{
		if( '\\' == pFilePath[i] )
			pFilePath[i] = '/';
	}
}

//============================================================================--
//! flip forward slashes into back slashes
void	VxMakeBackSlashPath( const std::string & csFilePath )
{
	char * pTemp = (char *)csFilePath.c_str();
	size_t iLen = strlen( pTemp );
	for( size_t i = 0; i < iLen; i++ )
	{
		if( '/' == pTemp[i] )
			pTemp[i] = '\\';
	}
}

//============================================================================--
//! flip forward slashes into back slashes
void	VxMakeBackSlashPath( const std::wstring & csFilePath )
{
	wchar_t * pTemp = (wchar_t *)csFilePath.c_str();
	size_t iLen = wstrlen( pTemp );
	for( size_t i = 0; i < iLen; i++ )
	{
		if( '/' == pTemp[i] )
			pTemp[i] = '\\';
	}
}

//============================================================================--
//! return true if last char is '/' else '\\'
bool VxDoesPathEndWithSlash( const char * pFileName )
{
	int iStrLen = strlen( pFileName );
	if( ('/' == pFileName[iStrLen-1]) ||
		('\\' == pFileName[iStrLen-1]) )
	{
		return true;
	}
	return false;
}

//============================================================================--
//! return true if is a root path like C:\dir or /dir
bool VxIsFullPath( const char * pFileName )
{
	bool isFullPath = false;
	if( pFileName )
	{
		int iStrLen = strlen( pFileName );
		if( iStrLen )
		{
			if('/' == pFileName[0] )
			{
				// linux full path
				isFullPath = true;
			}
			else if( iStrLen > 1 )
			{
				if( ':' == pFileName[ 1 ] )
				{
					// windows full path
					isFullPath = true;
				}
			}
		}
	}
	return isFullPath;
}

//============================================================================--
//! Make full path to execute directory if full path was not specified
//! NOTE: be careful .. assumes pFileName has enough space for full path and file name
void VxMakeFullPath( char * pFileName )
{
	if( false == VxIsFullPath(pFileName) )
	{
		// add path to the data directory
		std::string csFullPath;
		VxGetExecuteDirectory( csFullPath );
		csFullPath += pFileName;
		strcpy( pFileName, csFullPath.c_str() );
	}
}
//============================================================================--
//! Make full path to given directory if full path was not specified.. make path if does not exist
void VxMakeFullPath( const char * pShortFileName, const char * pDownloadDir, std::string & strRetPath )
{
	if( VxIsFullPath( pShortFileName ) )
	{
		strRetPath = pShortFileName;
	}
	else
	{
		strRetPath = pDownloadDir;
		if( false == VxDoesPathEndWithSlash(pDownloadDir))
		{
			strRetPath += "/";
		}
		strRetPath += pShortFileName;
	}
}

//============================================================================--
//! Make short FileName.. if pDownloadDir and full path contains pDownloadDir then will be path in that dir else just filename
bool VxMakeShortFileName( const char * pFullFileName, std::string & strRetShortName, const char * pDownloadDir )
{
	bool bUsedDownloadDir = false;
	if( VxIsFullPath( pDownloadDir ) )
	{
		int iDirStrLen = strlen( pDownloadDir );
		int iFileStrLen = strlen( pFullFileName );
		if( iFileStrLen > iDirStrLen )
		{
			if( 0 == strncmp( pDownloadDir, pFullFileName, iDirStrLen ) )
			{
				strRetShortName = &pFullFileName[ iDirStrLen ];
				bUsedDownloadDir = true;
			}
			else
			{
				VxGetJustFileName( pFullFileName, strRetShortName);
			}
		}
		else
		{
			// return just file name
			VxGetJustFileName( pFullFileName, strRetShortName);
		}
	}
	else if( VxIsFullPath( pFullFileName ) )
	{
		// return just file name
		std::string strFileName;
		std::string strPath;
		RCODE rc = VxSeperatePathAndFile(	pFullFileName,	// path and file name			
			strPath,		// return path to file
			strFileName );	// return file name
		if( 0 == rc )
		{
			//! separate Path and file name into separate strings
			strRetShortName = strFileName;
		}
		else
		{
			log_msg( LOG_ERROR, "VxMakeShortFileName: invalid file %s", pFullFileName );
			strRetShortName = pFullFileName; 
		}
	}
	else
	{
		strRetShortName = pFullFileName; 
	}
	return bUsedDownloadDir;
}

//============================================================================--
//! Get execution full path
RCODE	VxGetExecuteFullPathAndName( std::string& strRetExePathAndFileName )
{
	std::string strRetExeDir;
	std::string strRetExeFileName;
	RCODE rc = VxGetExecutePathAndName( strRetExeDir, strRetExeFileName );
	strRetExePathAndFileName = strRetExeDir + strRetExeFileName;
	return rc;
}

//============================================================================--
//! Get directory we execute from
RCODE	VxGetExecuteDirectory( std::string& strRetExeDir )
{
	std::string strRetExeFileName;
	return VxGetExecutePathAndName( strRetExeDir, strRetExeFileName );
}

//============================================================================--
//! Get execution path and file name
RCODE	VxGetExecutePathAndName( std::string& strRetExeDir, std::string& strRetExeFileName )
{
#ifdef WIN_PLATFORM
	char pRetBuf[ MAX_PATH ];
	int iRetStrLen = GetModuleFileName( NULL, pRetBuf, MAX_PATH );
	if( 0 != iRetStrLen )
	{
		// remove file name
		char * pTemp = strrchr( pRetBuf, '\\' );
		if( pTemp )
		{
			* pTemp = 0;
			pTemp++;
			strRetExeFileName = pTemp;
		}
		// remove debug path if exists
		pTemp = strrchr( pRetBuf, '\\' );
		if( pTemp )
		{
#ifdef _DEBUG
			if( 0 == strcmp( pTemp, "\\DEBUG" ) ||
				0 == strcmp( pTemp, "\\Debug" ) )
			{
				*pTemp = 0;
			}
#endif
		}
		// make sure has the final slash
		if( pRetBuf[ strlen( pRetBuf ) - 1 ] != '\\' )
		{
			strcat( pRetBuf, "\\" );
		}
		// flip the slashes
		size_t uiStrLen = strlen( pRetBuf );
		for( size_t i = 0; i < uiStrLen; i++ )
		{
			if( '\\' == pRetBuf[ i ] )
			{
				pRetBuf[ i ] = '/';
			}
		}
		strRetExeDir = pRetBuf;
		return 0;
	}
	else
	{
		log_msg( 0, "Error %d occured getting module directory\n", VxGetLastError());
		return -1;
	}

#else // LINUX
	char pRetBuf[ MAX_PATH ];
	int iByteCount;
	char* pTempBuf;
	iByteCount = readlink("/proc/self/exe", pRetBuf, MAX_PATH);
	if(-1 == iByteCount)
	{
		log_msg( 0, "Error %d occured getting module directory\n", VxGetLastError());
		return -1;

	}
	pRetBuf[iByteCount] = '\0';

	if(NULL == (pTempBuf = strrchr(pRetBuf,'/')))
	{
		log_msg( 0, "Error %d occured getting module directory\n", VxGetLastError());
		return -1;
	}
	pTempBuf[1] = '\0';

	//const char * dir * name = dirname( pRetBuf );

	strRetExeFileName = &pTempBuf[2];
	strRetExeDir = pRetBuf;
#endif // LINUX
	return 0;
}

//============================================================================--
BOOL VxFileNameWildMatch( const char  * pMatchName, const char * pWildName )
{
//  test if a file name matches a file name pattern.
//   handles * and ? wildcard characters.

    int   bNoMatch = FALSE;
    int   bDone = FALSE;
    while (FALSE == bDone)
    {
        switch (*pWildName)
        {
            case '*':
                // skip to last . or end of Name
				if(strchr(pWildName, '.'))
				{
					while(strchr(pWildName, '.'))
					{
						pWildName = strchr(pWildName, '.');
						pWildName++;
					}
					//pWildName--;
				}
				else
				{
					while (* pWildName)
						pWildName++;
				}
                // skip to last . or end of Name
				if( strchr(pMatchName, '.'))
				{
					while( strchr(pMatchName, '.'))
					{
						pMatchName = strchr(pMatchName, '.');
						pMatchName++;
					}
					//pMatchName--;
				}
				else
				{
					while (* pMatchName)
						pMatchName++;
				}
                break;

            case '?':
                pWildName++;
                pMatchName++;
                break;

            case 0:
                if (*pMatchName != 0)
                    bNoMatch = TRUE;
                bDone = TRUE;
                break;

            default:
                if (toupper(* pWildName) == toupper(* pMatchName))
                {
                    pWildName++;
                    pMatchName++;
                }
                else
                {
                    bNoMatch = TRUE;
                    bDone = TRUE;
                }
                break;
        }
    }
	if( bNoMatch )
		return FALSE ;
	return TRUE;
}
//============================================================================--
//! allocate memory and read whole file into memory
//! NOTE: USER MUST DELETE THE RETURED POINTER OR MEMORY LEAK WILL OCCURE
RCODE	VxReadWholeFile(	const char *	pFileName,			// file to read	
							void **			ppvRetBuf,			// return allocated buffer it was read into
							U32 *			pu32RetLenOfFile )	// return length of file
{
	RCODE rc = 0;
	U32 u32Len = (U32)VxGetFileLen( pFileName );
	if( 0 == u32Len )
	{
		rc = VxGetLastError();
		if( 0 == rc )
		{
			rc = -1;
		}
		log_msg( LOG_ERROR, "VxReadWholeFile: Could not open file %s", pFileName );
		return rc;
	}
	else
	{
		char * pTemp = new char[ u32Len + 16 ];
		RCODE rc = VxReadWholeFile( pFileName, pTemp, u32Len );
		if( rc )
		{
			// error occurred so delete so no memory leak
			delete pTemp;
			* ppvRetBuf = 0;
			* pu32RetLenOfFile = 0;
		}
		else
		{
			// make sure is null terminated
			pTemp[ u32Len ] = 0;
			* ppvRetBuf = pTemp;
			* pu32RetLenOfFile = u32Len;
		}
		return rc;
	}
}
//============================================================================--
//! read whole file of known length into existing buffer
//! NOTE assumes buffer has enough room for the whole file
RCODE VxReadWholeFile(	const char *	pFileName,				// file to read
						void *			pvBuf,					// buffer to read into
						U32				u32LenToRead,			// length to read ( assumes is same as file length
						U32	*			pu32RetAmountRead )		// return length actually read if not null
{
	RCODE rc = 0;
	if( pu32RetAmountRead  )
	{
		* pu32RetAmountRead = 0;
	}
	FILE * poFile = fopen( pFileName, "rb" );
	if( NULL == poFile )
	{
		rc = VxGetLastError();
		log_msg( 0, "VxReadWholeFile: error %d opening file %s\n", rc, pFileName );
		return rc;
	}
	size_t iResult = fread( pvBuf, 1, u32LenToRead, poFile );
	fclose( poFile );
	if( iResult > 0 )
	{
		if( pu32RetAmountRead  )
		{
			* pu32RetAmountRead = (U32)iResult;
		}
		// null terminate it
		if( u32LenToRead > iResult )
		{
			((char *)pvBuf)[ iResult ] = 0;
		}
		return 0;
	}
	rc = VxGetLastError();
	if( 0 == rc )
	{
		rc = -1;
	}
	log_msg( 0, "VxReadWholeFile: error %d reading file %s\n", rc, pFileName );
	return rc;
}


